Overview

In this document we describe with simple example and model how neural networks work, in order to understand the technology that is enabling Artificial Intelligence implementation.

What we will show

We will build a Neural Network capable of classifying the Iris flower according to the three species Setosa, Versicolor and Virginica. We selected Iris species as it is commonly used to test classification algorithm.

As we want to focus on Sinmple Neural Network to keep it easy and understandable, we will base our classification on length and width of Petal and Sepal, according to the Iris Data set - see attribute information box -. It is in fact known that one can classify the Iris type based on those “features”. In contrast we will not develop an image recognition Convolutional Neural Network as we want to make the Neural Net simple and understandable.

FIGURE 1 - IRIS

“FIGURE 1 - IRIS”

So our input is the Iris Data set summarized here.

summary(iris)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300   versicolor:50  
 Median :5.800   Median :3.000   Median :4.350   Median :1.300   virginica :50  
 Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199                  
 3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800                  
 Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500                  

How we will do it

We will use a standard LEARN and TEST approach, like at school: you have exercises with solutions you go through to learn and verify you are learning, and then you have the test phase where you have the exercise and the teacher will verify your result. This approach is used with Neural Netowrk as well and it is called Supervised Learning. The learning phase is also called training phase.

For the training phase we will use a portion of the Iris Data set and the remaining part of the data set will be used for the testing phase. Let’s consider 66% of the data for training and 33% of the data for testing. As the entire data set consists of 150 rows, we will use 100 rows as exercises to train like in homework and 50 rows as exercises to test like in class tests.

So our “student” that is the Neural Network - remember that it doesn’t know anything about Iris Species - will be trained with 100 exercises, During this phase it will learn, just by “looking” at the example, you don’t need to explain anything. After this phase we will test it with the reamaining 50 exercises, but this time we will provide to the Neural Network only the Petal, Sepal, length, width and will ask to classify the Iris. We will then check the answers against the solution that we have.

# set.seed(10)
# iris[sample(1:150,10),]

During this phase it will learn, just by “looking” at the example, you don’t need to explain anything. After this phase we will test it with the reamaining 50 exercises, but this time we will provide to the Neural Network only the Petal, Sepal, length, width and will ask to classify the Iris. We will then check the answers against the solution that we have. So the test will be performed on 50 exercises. See the following picture:

FIGURE 2 - TRAINING AND TESTING PHASES

“FIGURE 2 - TRAINING AND TESTING PHASES”

# set.seed(150)
# Species = rep("?",10)
# cbind(iris[sample(1:150,10),1:4],Species)

So after the training we will ask the Neural Network to replace the question mark with the proper answer: Setosa, Versicolor, Virginica.

Doesn’t sound a bit magic?

That’s the magic of learning by Neural Network. Think about the magic of a child learning a language just by listening, and sometimes getting the parents correcting him. Neural Network works in a similar way, as they try to mimic how the brain works.

But now let’s see what’s a Neural Network.

What a Neural Network is

A Neural Network consists of neural units connected among them and connected with the world through input and output. So basically Neural Network has one Input layer, one outpur layer and several hidden layers. In the case all neural units are connected to all neural units of the following layer. Such kind of Neural Network is a Feed Forward Neural Network, in contrast to convolutional and recursive Neural Network that have a different architecture. Here we will focus on Feed Forward Neural Network.Each neural unit receives several numerical input, weight each input and sum the input, before sending to the output the numerical value is processed by a non linear function that is called activation function as showed in the following picture:

Neural Network Architecture

“Neural Network Architecture”

So the Neural Network is basically defined by:

Now let’s classify the Iris!

As first step we have to create the Neural Network capable of classifing the Iris species. We will be using a standard method based on supervised learning, exactly using 66% of the Iris Data Set above, the 33% Data Set will be use to Validate the Neural Network.

So basicall the Neural Network will learn from examples and than will be evaluated with a classification test.

THe study is based on the R package nnet, that is a simple package that support only one hidden layer but it is powerful enough to train good models and for tutorial reasons.

Let’s split the Iris data in trainig set to learn and testing set to test, according to 66% - 33% ratio.

set.seed(180)
i <- sample(1:150,100)
iris_train <- iris[i,]
iris_test <- iris[-i,]
summary(iris_train)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width         Species  
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.10   setosa    :36  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.500   1st Qu.:0.20   versicolor:37  
 Median :5.700   Median :3.000   Median :4.100   Median :1.30   virginica :27  
 Mean   :5.811   Mean   :3.084   Mean   :3.607   Mean   :1.11                  
 3rd Qu.:6.325   3rd Qu.:3.400   3rd Qu.:5.025   3rd Qu.:1.80                  
 Max.   :7.900   Max.   :4.400   Max.   :6.700   Max.   :2.50                  

Now we need 4 neural nodes in input fed by the Sepal and Petal length and width, and 3 neural nodes in output to specify the probability of each species. Let’s consider only one neural node in the hidden layer (only one hidden layer is considered here for simplicity), the activation function we consider is the sigmoid and then the resulting Neural Network looks as follow:

library(devtools)
source_url('https://gist.githubusercontent.com/fawda123/7471137/raw/466c1474d0a505ff044412703516c34f1a4684a5/nnet_plot_update.r')
SHA-1 hash of file is 74c80bd5ddbc17ab3ae5ece9c0ed9beb612e87ef
library(nnet)
set.seed(18)
irnn <- nnet(Species ~ ., data = iris_train, size = 1, maxit = 0)
# weights:  11
plot.nnet(irnn)
Loading required package: scales
Loading required package: reshape

So it basically model creation at this point consists of “tuning” the 11 weights and the bias value. This is obatain in an iterative way, starting with random numbers and then changing them with a method called backpropagation in order to minimize the output error, so to minimize the misclassification. The iris_train data is used several time to obtain the convergence. Let’s have a look at how such network works before “learning”, so before the weights are tuned. It means basically that we are using random numbers.

irpred <- predict(irnn, iris_train, type = "class")
table(iris_train$Species, irpred)
            irpred
             setosa
  setosa         36
  versicolor     37
  virginica      27
sum(iris_train$Species == irpred) / length(iris_train$Species)
[1] 0.36

Unsurprisingly the accuracy is 36%, that is basically random choice of the Iris type. Now lets apply one cycle learning and see if it improves, so if it learns.

library(nnet)
set.seed(18)
irnn <- nnet(Species ~ ., data = iris_train, size = 1, maxit = 1)
# weights:  11
initial  value 111.716872 
final  value 111.000802 
stopped after 2 iterations
irpred <- predict(irnn, iris_train, type = "class")
table(iris_train$Species, irpred)
            irpred
             setosa
  setosa         36
  versicolor     37
  virginica      27
sum(iris_train$Species == irpred) / length(iris_train$Species)
[1] 0.36

Well it seems that the first learning cycle didn’t improve, so let the Neural Network play and play again with the data set as Romans said “Repetita Iuvant”: repeating things help! Let’s apply the default nnet package 100 repetition.

set.seed(18)
irnn <- nnet(Species ~ ., data = iris_train, size = 1)
# weights:  11
initial  value 111.716872 
iter  10 value 48.716977
iter  20 value 43.578253
final  value 43.577037 
converged
irpred <- predict(irnn, iris_train, type = "class")
table(iris_train$Species, irpred)
            irpred
             setosa versicolor
  setosa         36          0
  versicolor      0         37
  virginica       0         27
sum(iris_train$Species == irpred) / length(iris_train$Species)
[1] 0.73

Good the Neural Network improved a lot! Now it classifies the Iris species with 73% accuracy! At least in the iris_train. But now let’s see if it can understand Species from a new data set, never seen before, not used for traning, the iris_test.

irpred <- predict(irnn, iris_test, type = "class")
table(iris_test$Species, irpred)
            irpred
             setosa versicolor
  setosa         14          0
  versicolor      0         13
  virginica       0         23
sum(iris_test$Species == irpred) / length(iris_test$Species)
[1] 0.54

Good! We are 54% with only one neural node. Let’s have a look at it, note how the weights changed as you can see with the size of the links between neural nodes.

plot.nnet(irnn)

Now let’s try with a more complex Neural Network, in this case we increase the number of Neural Nodes in the hidden layer to 3

set.seed(18)
irnn <- nnet(Species ~ ., data = iris_train, size = 3)
# weights:  27
initial  value 124.056864 
iter  10 value 27.920209
iter  20 value 0.210898
iter  30 value 0.002851
final  value 0.000071 
converged
irpred <- predict(irnn, iris_train, type = "class")
table(iris_train$Species, irpred)
            irpred
             setosa versicolor virginica
  setosa         36          0         0
  versicolor      0         37         0
  virginica       0          0        27
sum(iris_train$Species == irpred) / length(iris_train$Species)
[1] 1

Very good improvement! We are now at 100% in the training set. This is not surprisingly we imporved, in fact the second models has higher number of neural nodes and connections among them. So basically we evolved the species! Let’s test it against testing set.

irpred <- predict(irnn, iris_test, type = "class")
table(iris_test$Species, irpred)
            irpred
             setosa versicolor virginica
  setosa         14          0         0
  versicolor      0         11         2
  virginica       0          0        23
sum(iris_test$Species == irpred) / length(iris_test$Species)
[1] 0.96

Doing very well! 96% Accuracy. Let’s have a look at the Neural Net:

plot.nnet(irnn)

Conclusion

We have trained a simple Neural Network based on available data that teaches how to classify the Iris species based on sepal-petal length and width. The training consisted of tuning weigths in the Neural Network by going through the trainig set several time. This process is pretty similar to a human learning process, where the man is trained with a controlled environemnt and he can check his results with actual results.

After the training is completed, meaning that the man is making a limited number of errors, he is ready to face real cases, that is cases where he doesn’t know the answer and he has to apply what he learnt.

This is exaclty what we did here with the simple Neural Net exercize. Now the Neural Net can classify Iris. Note also the effect of more neural nodes in the hidden layer that improved the accuracy from 54% to 96%. Note also that an untrained netowork is providing random guess.

LS0tDQp0aXRsZTogJ0FydGlmaWNpYWwgSW50ZWxsaWdlbmNlOiBhIE5ldXJhbCBOZXR3b3JrIFR1dG9yaWFsJw0KYXV0aG9yOiAiTHVjYSBWaWduYWxpIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQojIE92ZXJ2aWV3DQpJbiB0aGlzIGRvY3VtZW50IHdlIGRlc2NyaWJlIHdpdGggc2ltcGxlIGV4YW1wbGUgYW5kIG1vZGVsIGhvdyBuZXVyYWwgbmV0d29ya3Mgd29yaywgaW4gb3JkZXIgdG8gdW5kZXJzdGFuZCB0aGUgdGVjaG5vbG9neSB0aGF0IGlzIGVuYWJsaW5nIEFydGlmaWNpYWwgSW50ZWxsaWdlbmNlIGltcGxlbWVudGF0aW9uLg0KDQojIFdoYXQgd2Ugd2lsbCBzaG93DQpXZSB3aWxsIGJ1aWxkIGEgTmV1cmFsIE5ldHdvcmsgY2FwYWJsZSBvZiBjbGFzc2lmeWluZyB0aGUgSXJpcyBmbG93ZXIgYWNjb3JkaW5nIHRvIHRoZSB0aHJlZSBzcGVjaWVzIFNldG9zYSwgVmVyc2ljb2xvciBhbmQgVmlyZ2luaWNhLiANCldlIHNlbGVjdGVkIElyaXMgc3BlY2llcyBhcyBpdCBpcyBjb21tb25seSB1c2VkIHRvIHRlc3QgY2xhc3NpZmljYXRpb24gYWxnb3JpdGhtLg0KDQpBcyB3ZSB3YW50IHRvIGZvY3VzIG9uIFNpbm1wbGUgTmV1cmFsIE5ldHdvcmsgdG8ga2VlcCBpdCBlYXN5IGFuZCB1bmRlcnN0YW5kYWJsZSwgd2Ugd2lsbCBiYXNlIG91ciBjbGFzc2lmaWNhdGlvbiBvbiBsZW5ndGggYW5kIHdpZHRoIG9mIFBldGFsIGFuZCBTZXBhbCwgYWNjb3JkaW5nIHRvIHRoZSBJcmlzIERhdGEgc2V0IC0gc2VlIGF0dHJpYnV0ZSBpbmZvcm1hdGlvbiBib3ggLS4gSXQgaXMgaW4gZmFjdCBrbm93biB0aGF0IG9uZSBjYW4gY2xhc3NpZnkgdGhlIElyaXMgdHlwZSBiYXNlZCBvbiB0aG9zZSAiZmVhdHVyZXMiLg0KSW4gY29udHJhc3Qgd2Ugd2lsbCBub3QgZGV2ZWxvcCBhbiBpbWFnZSByZWNvZ25pdGlvbiBDb252b2x1dGlvbmFsIE5ldXJhbCBOZXR3b3JrIGFzIHdlIHdhbnQgdG8gbWFrZSB0aGUgTmV1cmFsIE5ldCBzaW1wbGUgYW5kIHVuZGVyc3RhbmRhYmxlLg0KDQohWyJGSUdVUkUgMSAtIElSSVMiXSguL0lSSVMucG5nKSANCg0KIA0KDQoNClNvIG91ciBpbnB1dCBpcyB0aGUgSXJpcyBEYXRhIHNldCBzdW1tYXJpemVkIGhlcmUuDQoNCmBgYHtyfQ0Kc3VtbWFyeShpcmlzKQ0KYGBgDQoNCiMgSG93IHdlIHdpbGwgZG8gaXQNCldlIHdpbGwgdXNlIGEgc3RhbmRhcmQgTEVBUk4gYW5kIFRFU1QgYXBwcm9hY2gsIGxpa2UgYXQgc2Nob29sOiB5b3UgaGF2ZSBleGVyY2lzZXMgd2l0aCBzb2x1dGlvbnMgeW91IGdvIHRocm91Z2ggdG8gbGVhcm4gYW5kIHZlcmlmeSB5b3UgYXJlIGxlYXJuaW5nLCBhbmQgdGhlbiB5b3UgaGF2ZSB0aGUgdGVzdCBwaGFzZSB3aGVyZSB5b3UgaGF2ZSB0aGUgZXhlcmNpc2UgYW5kIHRoZSB0ZWFjaGVyIHdpbGwgdmVyaWZ5IHlvdXIgcmVzdWx0LiBUaGlzIGFwcHJvYWNoIGlzIHVzZWQgd2l0aCBOZXVyYWwgTmV0b3dyayBhcyB3ZWxsIGFuZCBpdCBpcyBjYWxsZWQgU3VwZXJ2aXNlZCBMZWFybmluZy4gVGhlIGxlYXJuaW5nIHBoYXNlIGlzIGFsc28gY2FsbGVkIHRyYWluaW5nIHBoYXNlLg0KDQpGb3IgdGhlIHRyYWluaW5nIHBoYXNlIHdlIHdpbGwgdXNlIGEgcG9ydGlvbiBvZiB0aGUgSXJpcyBEYXRhIHNldCBhbmQgdGhlIHJlbWFpbmluZyBwYXJ0IG9mIHRoZSBkYXRhIHNldCB3aWxsIGJlIHVzZWQgZm9yIHRoZSB0ZXN0aW5nIHBoYXNlLiBMZXQncyBjb25zaWRlciA2NiUgb2YgdGhlIGRhdGEgZm9yIHRyYWluaW5nIGFuZCAzMyUgb2YgdGhlIGRhdGEgZm9yIHRlc3RpbmcuIEFzIHRoZSBlbnRpcmUgZGF0YSBzZXQgY29uc2lzdHMgb2YgMTUwIHJvd3MsIHdlIHdpbGwgdXNlIDEwMCByb3dzIGFzIGV4ZXJjaXNlcyB0byB0cmFpbiBsaWtlIGluIGhvbWV3b3JrIGFuZCA1MCByb3dzIGFzIGV4ZXJjaXNlcyB0byB0ZXN0IGxpa2UgaW4gY2xhc3MgdGVzdHMuICANCg0KU28gb3VyICJzdHVkZW50IiB0aGF0IGlzIHRoZSBOZXVyYWwgTmV0d29yayAtIHJlbWVtYmVyIHRoYXQgaXQgZG9lc24ndCBrbm93IGFueXRoaW5nIGFib3V0IElyaXMgU3BlY2llcyAtIHdpbGwgYmUgdHJhaW5lZCB3aXRoIDEwMCBleGVyY2lzZXMsIER1cmluZyB0aGlzIHBoYXNlIGl0IHdpbGwgbGVhcm4sIGp1c3QgYnkgImxvb2tpbmciIGF0IHRoZSBleGFtcGxlLCB5b3UgZG9uJ3QgbmVlZCB0byBleHBsYWluIGFueXRoaW5nLiBBZnRlciB0aGlzIHBoYXNlIHdlIHdpbGwgdGVzdCBpdCB3aXRoIHRoZSByZWFtYWluaW5nIDUwIGV4ZXJjaXNlcywgYnV0IHRoaXMgdGltZSB3ZSB3aWxsIHByb3ZpZGUgdG8gdGhlIE5ldXJhbCBOZXR3b3JrIG9ubHkgdGhlIFBldGFsLCBTZXBhbCwgbGVuZ3RoLCB3aWR0aCBhbmQgd2lsbCBhc2sgdG8gY2xhc3NpZnkgdGhlIElyaXMuIFdlIHdpbGwgdGhlbiBjaGVjayB0aGUgYW5zd2VycyBhZ2FpbnN0IHRoZSBzb2x1dGlvbiB0aGF0IHdlIGhhdmUuDQoNCmBgYHtyfQ0KIyBzZXQuc2VlZCgxMCkNCiMgaXJpc1tzYW1wbGUoMToxNTAsMTApLF0NCmBgYA0KDQoNCkR1cmluZyB0aGlzIHBoYXNlIGl0IHdpbGwgbGVhcm4sIGp1c3QgYnkgImxvb2tpbmciIGF0IHRoZSBleGFtcGxlLCB5b3UgZG9uJ3QgbmVlZCB0byBleHBsYWluIGFueXRoaW5nLiBBZnRlciB0aGlzIHBoYXNlIHdlIHdpbGwgdGVzdCBpdCB3aXRoIHRoZSByZWFtYWluaW5nIDUwIGV4ZXJjaXNlcywgYnV0IHRoaXMgdGltZSB3ZSB3aWxsIHByb3ZpZGUgdG8gdGhlIE5ldXJhbCBOZXR3b3JrIG9ubHkgdGhlIFBldGFsLCBTZXBhbCwgbGVuZ3RoLCB3aWR0aCBhbmQgd2lsbCBhc2sgdG8gY2xhc3NpZnkgdGhlIElyaXMuIFdlIHdpbGwgdGhlbiBjaGVjayB0aGUgYW5zd2VycyBhZ2FpbnN0IHRoZSBzb2x1dGlvbiB0aGF0IHdlIGhhdmUuDQpTbyB0aGUgdGVzdCB3aWxsIGJlIHBlcmZvcm1lZCBvbiA1MCBleGVyY2lzZXMuIFNlZSB0aGUgZm9sbG93aW5nIHBpY3R1cmU6DQoNCiFbIkZJR1VSRSAyIC0gVFJBSU5JTkcgQU5EIFRFU1RJTkcgUEhBU0VTIl0oLi9UcmFpbmluZy1UZXN0aW5nLnBuZykgDQoNCmBgYHtyfQ0KIyBzZXQuc2VlZCgxNTApDQojIFNwZWNpZXMgPSByZXAoIj8iLDEwKQ0KIyBjYmluZChpcmlzW3NhbXBsZSgxOjE1MCwxMCksMTo0XSxTcGVjaWVzKQ0KYGBgDQoNCg0KU28gYWZ0ZXIgdGhlIHRyYWluaW5nIHdlIHdpbGwgYXNrIHRoZSBOZXVyYWwgTmV0d29yayB0byByZXBsYWNlIHRoZSBxdWVzdGlvbiBtYXJrIHdpdGggdGhlIHByb3BlciBhbnN3ZXI6IFNldG9zYSwgVmVyc2ljb2xvciwgVmlyZ2luaWNhLg0KDQpEb2Vzbid0IHNvdW5kIGEgYml0IG1hZ2ljPyANCg0KVGhhdCdzIHRoZSBtYWdpYyBvZiBsZWFybmluZyBieSBOZXVyYWwgTmV0d29yay4gVGhpbmsgYWJvdXQgdGhlIG1hZ2ljIG9mIGEgY2hpbGQgbGVhcm5pbmcgYSBsYW5ndWFnZSBqdXN0IGJ5IGxpc3RlbmluZywgYW5kIHNvbWV0aW1lcyBnZXR0aW5nIHRoZSBwYXJlbnRzIGNvcnJlY3RpbmcgaGltLg0KTmV1cmFsIE5ldHdvcmsgd29ya3MgaW4gYSBzaW1pbGFyIHdheSwgYXMgdGhleSB0cnkgdG8gbWltaWMgaG93IHRoZSBicmFpbiB3b3Jrcy4NCg0KQnV0IG5vdyBsZXQncyBzZWUgd2hhdCdzIGEgTmV1cmFsIE5ldHdvcmsuDQoNCg0KIyBXaGF0IGEgTmV1cmFsIE5ldHdvcmsgaXMNCkEgTmV1cmFsIE5ldHdvcmsgY29uc2lzdHMgb2YgbmV1cmFsIHVuaXRzIGNvbm5lY3RlZCBhbW9uZyB0aGVtIGFuZCBjb25uZWN0ZWQgd2l0aCB0aGUgd29ybGQgdGhyb3VnaCBpbnB1dCBhbmQgb3V0cHV0LiBTbyBiYXNpY2FsbHkgTmV1cmFsIE5ldHdvcmsgaGFzIG9uZSBJbnB1dCBsYXllciwgb25lIG91dHB1ciBsYXllciBhbmQgc2V2ZXJhbCBoaWRkZW4gbGF5ZXJzLiBJbiB0aGUgY2FzZSBhbGwgbmV1cmFsIHVuaXRzIGFyZSBjb25uZWN0ZWQgdG8gYWxsIG5ldXJhbCB1bml0cyBvZiB0aGUgZm9sbG93aW5nIGxheWVyLiBTdWNoIGtpbmQgb2YgTmV1cmFsIE5ldHdvcmsgaXMgYSBGZWVkIEZvcndhcmQgTmV1cmFsIE5ldHdvcmssIGluIGNvbnRyYXN0IHRvIGNvbnZvbHV0aW9uYWwgYW5kIHJlY3Vyc2l2ZSBOZXVyYWwgTmV0d29yayB0aGF0IGhhdmUgYSBkaWZmZXJlbnQgYXJjaGl0ZWN0dXJlLiBIZXJlIHdlIHdpbGwgZm9jdXMgb24gRmVlZCBGb3J3YXJkIE5ldXJhbCBOZXR3b3JrLkVhY2ggbmV1cmFsIHVuaXQgcmVjZWl2ZXMgc2V2ZXJhbCBudW1lcmljYWwgaW5wdXQsIHdlaWdodCBlYWNoIGlucHV0IGFuZCBzdW0gdGhlIGlucHV0LCBiZWZvcmUgc2VuZGluZyB0byB0aGUgb3V0cHV0IHRoZSBudW1lcmljYWwgdmFsdWUgaXMgcHJvY2Vzc2VkIGJ5IGEgbm9uIGxpbmVhciBmdW5jdGlvbiB0aGF0IGlzIGNhbGxlZCBhY3RpdmF0aW9uIGZ1bmN0aW9uIGFzIHNob3dlZCBpbiB0aGUgZm9sbG93aW5nIHBpY3R1cmU6DQoNCiFbIk5ldXJhbCBOZXR3b3JrIEFyY2hpdGVjdHVyZSJdKC4vTmV1cmFsTmV0d29yay5QTkcpDQoNCg0KU28gdGhlIE5ldXJhbCBOZXR3b3JrIGlzIGJhc2ljYWxseSBkZWZpbmVkIGJ5Og0KDQoqIE51bWJlciBvZiBOZXVyb25zIGluIGVhY2ggbGF5ZXIuDQoqIEhvdyBtYW55IGhpZGRlbiBsYXllcnMgYXJlIHByZXNlbnQuDQoqIFRoZSB3ZWlndGggb2YgZWFjaCBpbnRlcmNvbm5lY3Rpb24uDQoqIFRoZSBhY3RpdmF0aW9uIGZ1bmN0aW9uIGF0IGVhY2ggbmV1cm9uLg0KKiBUaGUgYmlhcyBpbnB1dCB0byBlYWNoIGxheWVyIC0gZXhjbHVkaW5nIGlucHV0IGxheWVyDQoNCg0KIyBOb3cgbGV0J3MgY2xhc3NpZnkgdGhlIElyaXMhDQoNCkFzIGZpcnN0IHN0ZXAgd2UgaGF2ZSB0byBjcmVhdGUgdGhlIE5ldXJhbCBOZXR3b3JrIGNhcGFibGUgb2YgY2xhc3NpZmluZyB0aGUgSXJpcyBzcGVjaWVzLiBXZSB3aWxsIGJlIHVzaW5nIGEgc3RhbmRhcmQgbWV0aG9kIGJhc2VkIG9uIHN1cGVydmlzZWQgbGVhcm5pbmcsIGV4YWN0bHkgdXNpbmcgNjYlIG9mIHRoZSBJcmlzIERhdGEgU2V0IGFib3ZlLCB0aGUgMzMlIERhdGEgU2V0IHdpbGwgYmUgdXNlIHRvIFZhbGlkYXRlIHRoZSBOZXVyYWwgTmV0d29yay4NCg0KU28gYmFzaWNhbGwgdGhlIE5ldXJhbCBOZXR3b3JrIHdpbGwgbGVhcm4gZnJvbSBleGFtcGxlcyBhbmQgdGhhbiB3aWxsIGJlIGV2YWx1YXRlZCB3aXRoIGEgY2xhc3NpZmljYXRpb24gdGVzdC4NCg0KVEhlIHN0dWR5IGlzIGJhc2VkIG9uIHRoZSBSIHBhY2thZ2Ugbm5ldCwgdGhhdCBpcyBhIHNpbXBsZSBwYWNrYWdlIHRoYXQgc3VwcG9ydCBvbmx5IG9uZSBoaWRkZW4gbGF5ZXIgYnV0IGl0IGlzIHBvd2VyZnVsIGVub3VnaCB0byB0cmFpbiBnb29kIG1vZGVscyBhbmQgZm9yIHR1dG9yaWFsIHJlYXNvbnMuDQoNCkxldCdzIHNwbGl0IHRoZSBJcmlzIGRhdGEgaW4gdHJhaW5pZyBzZXQgdG8gbGVhcm4gYW5kIHRlc3Rpbmcgc2V0IHRvIHRlc3QsIGFjY29yZGluZyB0byA2NiUgLSAzMyUgcmF0aW8uDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTgwKQ0KaSA8LSBzYW1wbGUoMToxNTAsMTAwKQ0KaXJpc190cmFpbiA8LSBpcmlzW2ksXQ0KaXJpc190ZXN0IDwtIGlyaXNbLWksXQ0Kc3VtbWFyeShpcmlzX3RyYWluKQ0KYGBgDQoNCk5vdyB3ZSBuZWVkIDQgbmV1cmFsIG5vZGVzIGluIGlucHV0IGZlZCBieSB0aGUgU2VwYWwgYW5kIFBldGFsIGxlbmd0aCBhbmQgd2lkdGgsIGFuZCAzIG5ldXJhbCBub2RlcyBpbiBvdXRwdXQgdG8gc3BlY2lmeSB0aGUgcHJvYmFiaWxpdHkgb2YgZWFjaCBzcGVjaWVzLg0KTGV0J3MgY29uc2lkZXIgb25seSBvbmUgbmV1cmFsIG5vZGUgaW4gdGhlIGhpZGRlbiBsYXllciAob25seSBvbmUgaGlkZGVuIGxheWVyIGlzIGNvbnNpZGVyZWQgaGVyZSBmb3Igc2ltcGxpY2l0eSksIHRoZSBhY3RpdmF0aW9uIGZ1bmN0aW9uIHdlIGNvbnNpZGVyIGlzIHRoZSBzaWdtb2lkIGFuZCB0aGVuIHRoZSByZXN1bHRpbmcgTmV1cmFsIE5ldHdvcmsgbG9va3MgYXMgZm9sbG93Og0KDQpgYGB7cn0NCmxpYnJhcnkoZGV2dG9vbHMpDQpzb3VyY2VfdXJsKCdodHRwczovL2dpc3QuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2Zhd2RhMTIzLzc0NzExMzcvcmF3LzQ2NmMxNDc0ZDBhNTA1ZmYwNDQ0MTI3MDM1MTZjMzRmMWE0Njg0YTUvbm5ldF9wbG90X3VwZGF0ZS5yJykNCmxpYnJhcnkobm5ldCkNCnNldC5zZWVkKDE4KQ0KaXJubiA8LSBubmV0KFNwZWNpZXMgfiAuLCBkYXRhID0gaXJpc190cmFpbiwgc2l6ZSA9IDEsIG1heGl0ID0gMCkNCnBsb3Qubm5ldChpcm5uKQ0KDQpgYGANCg0KU28gaXQgYmFzaWNhbGx5IG1vZGVsIGNyZWF0aW9uIGF0IHRoaXMgcG9pbnQgY29uc2lzdHMgb2YgInR1bmluZyIgdGhlIDExIHdlaWdodHMgYW5kIHRoZSBiaWFzIHZhbHVlLiBUaGlzIGlzIG9iYXRhaW4gaW4gYW4gaXRlcmF0aXZlIHdheSwgc3RhcnRpbmcgd2l0aCByYW5kb20gbnVtYmVycyBhbmQgdGhlbiBjaGFuZ2luZyB0aGVtIHdpdGggYSBtZXRob2QgY2FsbGVkIGJhY2twcm9wYWdhdGlvbiBpbiBvcmRlciB0byBtaW5pbWl6ZSB0aGUgb3V0cHV0IGVycm9yLCBzbyB0byBtaW5pbWl6ZSB0aGUgbWlzY2xhc3NpZmljYXRpb24uIFRoZSBpcmlzX3RyYWluIGRhdGEgaXMgdXNlZCBzZXZlcmFsIHRpbWUgdG8gb2J0YWluIHRoZSBjb252ZXJnZW5jZS4NCkxldCdzIGhhdmUgYSBsb29rIGF0IGhvdyBzdWNoIG5ldHdvcmsgd29ya3MgYmVmb3JlICJsZWFybmluZyIsIHNvIGJlZm9yZSB0aGUgd2VpZ2h0cyBhcmUgdHVuZWQuIEl0IG1lYW5zIGJhc2ljYWxseSB0aGF0IHdlIGFyZSB1c2luZyByYW5kb20gbnVtYmVycy4NCg0KYGBge3J9DQppcnByZWQgPC0gcHJlZGljdChpcm5uLCBpcmlzX3RyYWluLCB0eXBlID0gImNsYXNzIikNCnRhYmxlKGlyaXNfdHJhaW4kU3BlY2llcywgaXJwcmVkKQ0Kc3VtKGlyaXNfdHJhaW4kU3BlY2llcyA9PSBpcnByZWQpIC8gbGVuZ3RoKGlyaXNfdHJhaW4kU3BlY2llcykNCg0KYGBgDQoNClVuc3VycHJpc2luZ2x5IHRoZSBhY2N1cmFjeSBpcyAzNiUsIHRoYXQgaXMgYmFzaWNhbGx5IHJhbmRvbSBjaG9pY2Ugb2YgdGhlIElyaXMgdHlwZS4gTm93IGxldHMgYXBwbHkgb25lIGN5Y2xlIGxlYXJuaW5nIGFuZCBzZWUgaWYgaXQgaW1wcm92ZXMsIHNvIGlmIGl0IGxlYXJucy4NCg0KYGBge3J9DQpsaWJyYXJ5KG5uZXQpDQpzZXQuc2VlZCgxOCkNCmlybm4gPC0gbm5ldChTcGVjaWVzIH4gLiwgZGF0YSA9IGlyaXNfdHJhaW4sIHNpemUgPSAxLCBtYXhpdCA9IDEpDQppcnByZWQgPC0gcHJlZGljdChpcm5uLCBpcmlzX3RyYWluLCB0eXBlID0gImNsYXNzIikNCg0KdGFibGUoaXJpc190cmFpbiRTcGVjaWVzLCBpcnByZWQpDQpzdW0oaXJpc190cmFpbiRTcGVjaWVzID09IGlycHJlZCkgLyBsZW5ndGgoaXJpc190cmFpbiRTcGVjaWVzKQ0KYGBgDQoNCldlbGwgaXQgc2VlbXMgdGhhdCB0aGUgZmlyc3QgbGVhcm5pbmcgY3ljbGUgZGlkbid0IGltcHJvdmUsIHNvIGxldCB0aGUgTmV1cmFsIE5ldHdvcmsgcGxheSBhbmQgcGxheSBhZ2FpbiB3aXRoIHRoZSBkYXRhIHNldCBhcyBSb21hbnMgc2FpZCAiUmVwZXRpdGEgSXV2YW50IjogcmVwZWF0aW5nIHRoaW5ncyBoZWxwISBMZXQncyBhcHBseSB0aGUgZGVmYXVsdCBubmV0IHBhY2thZ2UgMTAwIHJlcGV0aXRpb24uDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTgpDQppcm5uIDwtIG5uZXQoU3BlY2llcyB+IC4sIGRhdGEgPSBpcmlzX3RyYWluLCBzaXplID0gMSkNCmlycHJlZCA8LSBwcmVkaWN0KGlybm4sIGlyaXNfdHJhaW4sIHR5cGUgPSAiY2xhc3MiKQ0KDQoNCnRhYmxlKGlyaXNfdHJhaW4kU3BlY2llcywgaXJwcmVkKQ0Kc3VtKGlyaXNfdHJhaW4kU3BlY2llcyA9PSBpcnByZWQpIC8gbGVuZ3RoKGlyaXNfdHJhaW4kU3BlY2llcykNCg0KYGBgDQoNCkdvb2QgdGhlIE5ldXJhbCBOZXR3b3JrIGltcHJvdmVkIGEgbG90ISBOb3cgaXQgY2xhc3NpZmllcyB0aGUgSXJpcyBzcGVjaWVzIHdpdGggNzMlIGFjY3VyYWN5ISBBdCBsZWFzdCBpbiB0aGUgaXJpc190cmFpbi4gQnV0IG5vdyBsZXQncyBzZWUgaWYgaXQgY2FuIHVuZGVyc3RhbmQgU3BlY2llcyBmcm9tIGEgbmV3IGRhdGEgc2V0LCBuZXZlciBzZWVuIGJlZm9yZSwgbm90IHVzZWQgZm9yIHRyYW5pbmcsIHRoZSBpcmlzX3Rlc3QuDQoNCmBgYHtyfQ0KaXJwcmVkIDwtIHByZWRpY3QoaXJubiwgaXJpc190ZXN0LCB0eXBlID0gImNsYXNzIikNCnRhYmxlKGlyaXNfdGVzdCRTcGVjaWVzLCBpcnByZWQpDQpzdW0oaXJpc190ZXN0JFNwZWNpZXMgPT0gaXJwcmVkKSAvIGxlbmd0aChpcmlzX3Rlc3QkU3BlY2llcykNCmBgYA0KDQpHb29kISBXZSBhcmUgNTQlIHdpdGggb25seSBvbmUgbmV1cmFsIG5vZGUuIExldCdzIGhhdmUgYSBsb29rIGF0IGl0LCBub3RlIGhvdyB0aGUgd2VpZ2h0cyBjaGFuZ2VkIGFzIHlvdSBjYW4gc2VlIHdpdGggdGhlIHNpemUgb2YgdGhlIGxpbmtzIGJldHdlZW4gbmV1cmFsIG5vZGVzLg0KDQpgYGB7cn0NCnBsb3Qubm5ldChpcm5uKQ0KYGBgDQoNCk5vdyBsZXQncyB0cnkgd2l0aCBhIG1vcmUgY29tcGxleCBOZXVyYWwgTmV0d29yaywgaW4gdGhpcyBjYXNlIHdlIGluY3JlYXNlIHRoZSBudW1iZXIgb2YgTmV1cmFsIE5vZGVzIGluIHRoZSBoaWRkZW4gbGF5ZXIgdG8gMw0KDQpgYGB7cn0NCnNldC5zZWVkKDE4KQ0KaXJubiA8LSBubmV0KFNwZWNpZXMgfiAuLCBkYXRhID0gaXJpc190cmFpbiwgc2l6ZSA9IDMpDQppcnByZWQgPC0gcHJlZGljdChpcm5uLCBpcmlzX3RyYWluLCB0eXBlID0gImNsYXNzIikNCg0KDQp0YWJsZShpcmlzX3RyYWluJFNwZWNpZXMsIGlycHJlZCkNCnN1bShpcmlzX3RyYWluJFNwZWNpZXMgPT0gaXJwcmVkKSAvIGxlbmd0aChpcmlzX3RyYWluJFNwZWNpZXMpDQpgYGANCg0KVmVyeSBnb29kIGltcHJvdmVtZW50ISBXZSBhcmUgbm93IGF0IDEwMCUgaW4gdGhlIHRyYWluaW5nIHNldC4gVGhpcyBpcyBub3Qgc3VycHJpc2luZ2x5IHdlIGltcG9ydmVkLCBpbiBmYWN0IHRoZSBzZWNvbmQgbW9kZWxzIGhhcyBoaWdoZXIgbnVtYmVyIG9mIG5ldXJhbCBub2RlcyBhbmQgY29ubmVjdGlvbnMgYW1vbmcgdGhlbS4gU28gYmFzaWNhbGx5IHdlIGV2b2x2ZWQgdGhlIHNwZWNpZXMhDQpMZXQncyB0ZXN0IGl0IGFnYWluc3QgdGVzdGluZyBzZXQuDQoNCmBgYHtyfQ0KaXJwcmVkIDwtIHByZWRpY3QoaXJubiwgaXJpc190ZXN0LCB0eXBlID0gImNsYXNzIikNCnRhYmxlKGlyaXNfdGVzdCRTcGVjaWVzLCBpcnByZWQpDQpzdW0oaXJpc190ZXN0JFNwZWNpZXMgPT0gaXJwcmVkKSAvIGxlbmd0aChpcmlzX3Rlc3QkU3BlY2llcykNCg0KYGBgDQoNCkRvaW5nIHZlcnkgd2VsbCEgOTYlIEFjY3VyYWN5LiBMZXQncyBoYXZlIGEgbG9vayBhdCB0aGUgTmV1cmFsIE5ldDoNCg0KYGBge3J9DQpwbG90Lm5uZXQoaXJubikNCmBgYA0KDQoNCiMgQ29uY2x1c2lvbg0KV2UgaGF2ZSB0cmFpbmVkIGEgc2ltcGxlIE5ldXJhbCBOZXR3b3JrIGJhc2VkIG9uIGF2YWlsYWJsZSBkYXRhIHRoYXQgdGVhY2hlcyBob3cgdG8gY2xhc3NpZnkgdGhlIElyaXMgc3BlY2llcyBiYXNlZCBvbiBzZXBhbC1wZXRhbCBsZW5ndGggYW5kIHdpZHRoLiBUaGUgdHJhaW5pbmcgY29uc2lzdGVkIG9mIHR1bmluZyB3ZWlndGhzIGluIHRoZSBOZXVyYWwgTmV0d29yayBieSBnb2luZyB0aHJvdWdoIHRoZSB0cmFpbmlnIHNldCBzZXZlcmFsIHRpbWUuIFRoaXMgcHJvY2VzcyBpcyBwcmV0dHkgc2ltaWxhciB0byBhIGh1bWFuIGxlYXJuaW5nIHByb2Nlc3MsIHdoZXJlIHRoZSBtYW4gaXMgdHJhaW5lZCB3aXRoIGEgY29udHJvbGxlZCBlbnZpcm9uZW1udCBhbmQgaGUgY2FuIGNoZWNrIGhpcyByZXN1bHRzIHdpdGggYWN0dWFsIHJlc3VsdHMuIA0KDQpBZnRlciB0aGUgdHJhaW5pbmcgaXMgY29tcGxldGVkLCBtZWFuaW5nIHRoYXQgdGhlIG1hbiBpcyBtYWtpbmcgYSBsaW1pdGVkIG51bWJlciBvZiBlcnJvcnMsIGhlIGlzIHJlYWR5IHRvIGZhY2UgcmVhbCBjYXNlcywgdGhhdCBpcyBjYXNlcyB3aGVyZSBoZSBkb2Vzbid0IGtub3cgdGhlIGFuc3dlciBhbmQgaGUgaGFzIHRvIGFwcGx5IHdoYXQgaGUgbGVhcm50Lg0KDQpUaGlzIGlzIGV4YWNsdHkgd2hhdCB3ZSBkaWQgaGVyZSB3aXRoIHRoZSBzaW1wbGUgTmV1cmFsIE5ldCBleGVyY2l6ZS4gTm93IHRoZSBOZXVyYWwgTmV0IGNhbiBjbGFzc2lmeSBJcmlzLiBOb3RlIGFsc28gdGhlIGVmZmVjdCBvZiBtb3JlIG5ldXJhbCBub2RlcyBpbiB0aGUgaGlkZGVuIGxheWVyIHRoYXQgaW1wcm92ZWQgdGhlIGFjY3VyYWN5IGZyb20gNTQlIHRvIDk2JS4gTm90ZSBhbHNvIHRoYXQgYW4gdW50cmFpbmVkIG5ldG93b3JrIGlzIHByb3ZpZGluZyByYW5kb20gZ3Vlc3MuDQoNCg0KDQoNCg0KDQoNCg0KDQo=